import { color } from "../../../core/color";
import { any } from "../../../core/logic";
import { Engine } from "../../../Engine";
import { Pass } from "../Pass";
import { Effect, EffectType } from "./Effect";
import { fog_frag } from "./shaders/fog_frag";
import { fog_vert } from "./shaders/fog_vert";
import { foggbuffer_frag } from "./shaders/foggbuffer_frag";
/**
 * 后处理全局雾效
 * @class
 * @memberof w.effects
 */
class GlobalFog extends Effect {
    /**
     * @constructor
     * @param opts
     * @param opts.depthFogType {Number} 【可选，缺省:3.0】深度雾类型：0.0:不使用深度雾，1.0:距离深度雾，2.0:指数深度雾，3.0:指数平方深度雾。
     * @param opts.depthFogDensity {Number}【可选，缺省:0.001】深度雾强度,当depthFogType=2.0|3.0时有效。
     * @param opts.depthFogStart {Number}【可选，缺省:1】深度雾开始距离,当depthFogType=1.0时有效。
     * @param opts.depthFogEnd {Number}【可选，缺省:100000】深度雾结束距离,当depthFogType=1.0时有效。
     * @param opts.depthFogColor {String}【可选，缺省:"#ffffff"】深度雾颜色。
     * @param opts.depthFogAtten {Number}【可选，缺省:1.0】深度雾衰减。
     */
    constructor(opts = {}) {
        super(opts);
        this.type = EffectType.GlobalFog;
        this.sampler = Engine.instance.device.createSampler({
            magFilter: 'linear',//nearest linear
            minFilter: 'linear'
        });
        this.initBuffer();
        this.initPipeLine();
        //深度雾部分
        this.depthFogType = any(opts.depthFogType, 3.0);
        this.depthFogDensity = any(opts.depthFogDensity, 0.001);
        this.depthFogStart = any(opts.depthFogStart, 1);
        this.depthFogEnd = any(opts.depthFogEnd, 100000);
        this.depthFogColor = any(opts.depthFogColor, "#ffffff");
        this.depthFogAtten = any(opts.depthFogAtten, 1.0);
        this.skyfogSmooth = any(opts.skyfogSmooth, 1.0);
        //高度雾部分
        this.heightFogType = any(opts.heightFogType, 0.0);//0:不启用  1：线性 2：指数 3：指数平方
        this.heightFogDensity = any(opts.heightFogDensity, 0.001);
        this.heightFogStart = any(opts.heightFogStart, 0);
        this.heightFogEnd = any(opts.heightFogEnd, 100);
        this.heightFogColor = any(opts.heightFogColor, "#ffffff");
        this.heightFogDistance = any(opts.heightFogDistance, 0.0);
        this.autoUpdate = true;
        this.update();
    }
    set depthFogType(v) {
        this._depthFogType = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            0,
            buffer
        );
        buffer = null;
        this.update();
    }
    get depthFogType() {
        return this._depthFogType;
    }
    set depthFogDensity(v) {
        this._depthFogDensity = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            4,
            buffer
        );
        buffer = null;
        this.update();
    }
    get depthFogDensity() {
        return this._depthFogDensity;
    }
    set depthFogStart(v) {
        this._depthFogStart = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            8,
            buffer
        );
        buffer = null;
        this.update();
    }
    get depthFogStart() {
        return this._depthFogStart;
    }
    set depthFogEnd(v) {
        this._depthFogEnd = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            12,
            buffer
        );
        buffer = null;
        this.update();
    }
    get depthFogEnd() {
        return this._depthFogEnd;
    }
    set depthFogColor(v) {
        this._depthFogColor = v;
        let buffer = color.format(v, false);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            16,
            buffer
        );
        buffer = null;
        this.update();
    }
    get depthFogColor() {
        return this._depthFogColor;
    }
    set depthFogAtten(v) {
        this._depthFogAtten = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            28,
            buffer
        );
        buffer = null;
        this.update();
    }
    get depthFogAtten() {
        return this._depthFogAtten;
    }
    get skyfogSmooth() {
        return this._skyfogSmooth;
    }
    set skyfogSmooth(v) {
        this._skyfogSmooth = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.depthFogBuffer,
            32,
            buffer
        );
        buffer = null;
        this.update();
    }
    set heightFogType(v) {
        this._heightFogType = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.heightFogBuffer,
            0,
            buffer
        );
        buffer = null;
        this.update();
    }
    get heightFogType() {
        return this._heightFogType;
    }
    set heightFogDensity(v) {
        this._heightFogDensity = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.heightFogBuffer,
            4,
            buffer
        );
        buffer = null;
        this.update();
    }
    get heightFogDensity() {
        return this._heightFogDensity;
    }
    set heightFogStart(v) {
        this._heightFogStart = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.heightFogBuffer,
            8,
            buffer
        );
        buffer = null;
        this.update();
    }
    get heightFogStart() {
        return this._heightFogStart;
    }
    set heightFogEnd(v) {
        this._heightFogEnd = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.heightFogBuffer,
            12,
            buffer
        );
        buffer = null;
        this.update();
    }
    get heightFogEnd() {
        return this._heightFogEnd;
    }
    set heightFogColor(v) {
        this._heightFogColor = v;
        let buffer = color.format(v, false);
        Engine.instance.queue.writeBuffer(
            this.heightFogBuffer,
            16,
            buffer
        );
        buffer = null;
        this.update();
    }
    get heightFogColor() {
        return this._heightFogColor;
    }
    set heightFogDistance(v) {
        this._heightFogDistance = v;
        let buffer = new Float32Array([v]);
        Engine.instance.queue.writeBuffer(
            this.heightFogBuffer,
            28,
            buffer
        );
        buffer = null;
        this.update();
    }
    get heightFogDistance() {
        return this._heightFogDistance;
    }
    initBuffer() {
        this.depthFogBuffer = Engine.instance.device.createBuffer({
            label: "depthFog",
            size: 48,
            usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
        });
        this.heightFogBuffer = Engine.instance.device.createBuffer({
            label: "heightFog",
            size: 32,
            usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
        });
    }
    initPipeLine() {
        this.bindGroupLayout = Engine.instance.device.createBindGroupLayout({
            label: "layout",
            entries: [
                {
                    binding: 0,
                    visibility: GPUShaderStage.FRAGMENT,
                    sampler: {
                        type: "filtering"
                    },
                },
                {
                    binding: 1,
                    visibility: GPUShaderStage.FRAGMENT,
                    texture: {
                        sampleType: "float",
                        viewDimension: "2d",
                        multisampled: false
                    }
                }, {
                    binding: 2,
                    visibility: GPUShaderStage.FRAGMENT,
                    texture: {
                        sampleType: Engine.instance.enableGbuffer ? "float" : "depth",
                        viewDimension: "2d",
                        multisampled: false
                    }
                }, {
                    binding: 3,
                    visibility: GPUShaderStage.FRAGMENT,
                    buffer: {
                        type: "uniform"
                    },
                }, {
                    binding: 4,
                    visibility: GPUShaderStage.FRAGMENT,
                    buffer: {
                        type: "uniform"
                    },
                }, {
                    binding: 5,
                    visibility: GPUShaderStage.FRAGMENT,
                    buffer: {
                        type: "uniform"
                    },
                }
            ]
        });
        let pipeLayout = Engine.instance.device.createPipelineLayout({ bindGroupLayouts: [this.bindGroupLayout] });
        this.pipeLine = Engine.instance.device.createRenderPipeline({
            label: 'Fog Pipline',
            layout: pipeLayout,
            vertex: {
                module: Engine.instance.device.createShaderModule({
                    code: fog_vert,
                }),
                entryPoint: 'main'
            },
            fragment: {
                module: Engine.instance.device.createShaderModule({
                    code: Engine.instance.enableGbuffer ? foggbuffer_frag : fog_frag,
                }),
                entryPoint: 'main',
                targets: [
                    {
                        format: Engine.instance.format
                    }
                ]
            },
            primitive: {
                topology: 'triangle-list',
                cullMode: 'back',
                frontFace: 'ccw'
            }
        });
    }
    initBindGroup(texture) {
        if (this.group && this.group.id != texture.id) {
            this.group = null;
        }
        if (!this.group) {
            const cameraBuffer = Engine.instance.scene.getCamera().getParam("camera").gpu.buffer;
            this.group=Engine.instance.device.createBindGroup({
                label: 'Texture Group with Texture/Sampler',
                layout: this.bindGroupLayout,
                entries: [
                    {
                        binding: 0,
                        resource: this.sampler
                    },
                    {
                        binding: 1,
                        resource: texture.view
                    },
                    {
                        binding: 2,
                        resource: Engine.instance.enableGbuffer ? Pass.instance().postionTexture.view : Pass.instance().depthAttach.view
                    },
                    {
                        binding: 3,
                        resource: {
                            buffer: cameraBuffer,
                            size: 432,
                        }
                    },
                    {
                        binding: 4,
                        resource: {
                            buffer: this.depthFogBuffer,
                            size: 48,
                        }
                    },
                    {
                        binding: 5,
                        resource: {
                            buffer: this.heightFogBuffer,
                            size: 32,
                        }
                    }
                ]
            });
            this.group.id=texture.id;
        }
        return this.group;
    }
    onRender(commandEncoder) {
        //执行帧渲染
        const pass = super.onRender(commandEncoder);
        const texture = Pass.instance().readAttach;
        const renderPass = commandEncoder.beginRenderPass(pass);
        renderPass.setPipeline(this.pipeLine);
        let bindGroup = this.initBindGroup(texture);
        // set uniformGroup
        renderPass.setBindGroup(0, bindGroup)
        renderPass.draw(3)
        renderPass.end()
    }
    getDescript() {
        if (this._descript) {
            if (this._descript.size[0] !== Engine.instance.size[0] || this._descript.size[1] !== Engine.instance.size[1]) {
                this._descript = null;
            } else {
                this._descript.colorAttachments[0].view = Pass.instance().writeAttach.view;
            }
        }
        if (!this._descript) {
            this._descript = {
                colorAttachments: [
                    {
                        view: Pass.instance().writeAttach.view,
                        loadOp: 'clear',
                        clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
                        storeOp: 'store',
                    }
                ]
            }
            this._descript.size = Engine.instance.size;
        }
        return this._descript;
    }
}
export { GlobalFog }